using System;
using System.Drawing;
using Wilco.SyntaxHighlighting.Engine.Highlighter;
using Wilco.SyntaxHighlighting.Engine.Node.Implementation.String;
using Wilco.SyntaxHighlighting.Engine.Node.Implementation.Word;
using Wilco.SyntaxHighlighting.Engine.Node.Occurrence;
using Wilco.SyntaxHighlighting.Engine.Parser;
using Wilco.SyntaxHighlighting.Engine.Scanner;
using Wilco.SyntaxHighlighting.Engine.Scanner.Implementation;
using Wilco.SyntaxHighlighting.Engine.Tokenizer;

namespace Wilco.SyntaxHighlighting.Engine.Highlighter.Implementation
{
    /// <summary>
    /// Represents a C# syntax highlighter.
    /// </summary>
    public class CSharpHighlighter : HighlighterBase
    {
        /// <summary>
        /// Initializes a new instance of a <see cref="CSharpHighlighter"/> class.
        /// </summary>
        public CSharpHighlighter() : this(null)
        {
        }

        /// <summary>
        /// Initializes a new instance of a <see cref="CSharpHighlighter"/> class.
        /// </summary>
        /// <param name="parser">The parser which will be used to parse the source code.</param>
        public CSharpHighlighter(IParser parser) : base(parser)
        {
            Name = "C#";
            FullName = "C#";
            TagValues.AddRange(new String[] { "csharp", "c#", "cs" });
            FileExtensions.Add("cs");
        }

        /// <summary>
        /// Creates a new instance (clone) of this highlighter.
        /// </summary>
        /// <returns></returns>
        public override HighlighterBase Create()
        {
            return new CSharpHighlighter(Parser);
        }

        /// <summary>
        /// Builds a new chain of scanners.
        /// </summary>
        /// <param name="tokenizer">The tokenizer used by the scanners.</param>
        /// <param name="scannerResult">The scanner result.</param>
        /// <returns>
        /// The scanner at the start of the chain.
        /// </returns>
        public override IScanner BuildEntryPointScanner(TokenizerBase tokenizer, OccurrenceCollection scannerResult)
        {
            Tokenizer = tokenizer;
            ScannerResult = scannerResult;

            IScanner wordScanner = BuildWordScanner();

            IScanner stringLineScanner = BuildStringScanner();
            stringLineScanner.Child = wordScanner;

            IScanner stringBlockScanner = BuildStringBlockScanner();
            stringBlockScanner.Child = stringLineScanner;

            IScanner commentLineScanner = BuildCommentLineScanner();
            commentLineScanner.Child = stringBlockScanner;

            CommentLineScanner xmlCommentLineScanner = new CommentLineScanner(Tokenizer, ScannerResult);
            xmlCommentLineScanner.CommentLineNode.Entities.Add("///");
            xmlCommentLineScanner.CommentLineNode.ForeColor = Color.Gray;
            xmlCommentLineScanner.Child = commentLineScanner;

            IScanner commentBlockScanner = BuildCommentBlockScanner();
            commentBlockScanner.Child = xmlCommentLineScanner;

            return commentBlockScanner;
        }

        /// <summary>
        /// Builds a word scanner.
        /// </summary>
        /// <returns>A <see cref="WordScanner"/> object.</returns>
        protected override IScanner BuildWordScanner()
        {
            WordScanner scanner = new WordScanner(Tokenizer, ScannerResult);
            scanner.WordNodes = new WordNode[2];
            scanner.WordNodes[0] = new WordNode();
            scanner.WordNodes[0].ForeColor = Color.Blue;
            scanner.WordNodes[0].Entities.AddRange(GetKeywords());
            scanner.WordNodes[1] = new WordNode();
            scanner.WordNodes[1].ForeColor = Color.Red;
            scanner.WordNodes[1].Entities.AddRange(new String[] { "#region", "#endregion" });
            return scanner;
        }

        /// <summary>
        /// Builds a string block scanner.
        /// </summary>
        /// <returns>A <see cref="StringBlockScanner"/> object.</returns>
        protected virtual IScanner BuildStringBlockScanner()
        {
            StringBlockScanner blockScanner = new StringBlockScanner(Tokenizer, ScannerResult);
            blockScanner.StringNode.Entities.Add(new StringEntity("\"", "\"", "\""));
            blockScanner.StringNode.ForeColor = Color.DarkRed;
            return blockScanner;
        }

        /// <summary>
        /// Gets an array of registered keywords.
        /// </summary>
        /// <returns>An array of keywords.</returns>
        private static string[] GetKeywords()
        {
            string[] keywordList = new string[]
                {
                    "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked",
                    "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else",
                    "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for",
                    "foreach", "get", "goto", "if", "implicit", "in", "int", "interface", "internal", "is",
                    "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override",
                    "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed",
                    "set", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this",
                    "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort",
                    "using", "value", "virtual", "void", "volatile", "while"
                };

            Array.Sort(keywordList);
            Array.Reverse(keywordList);

            return keywordList;
        }
    }
}